package io.bitchat.http.converter;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * 基本类型和基本类型数组转换器
 * 能够转换的基本类型仅限于com.sitechasia.webx.core.utils.populator.PrimitiveType中所定义的类型
 *
 * @author houyi
 * @see {@link com.sitechasia.webx.core.utils.populator.PrimitiveTypeUtil}
 */
public final class PrimitiveConverter extends AbstractConverter {

    private static final PrimitiveConverter CONVERTER;

    static {
        CONVERTER = new PrimitiveConverter();
    }

    private static final char[] DIGITAL_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

    /**
     * 对基本类型进行类型转换
     */
    @Override
    @SuppressWarnings("unused")
    protected Object doConvertValue(Object source, Class<?> toType, Object... params) {
        /*
         * 如果是基础类型，则直接返回
         */
        if (source != null && (!PrimitiveTypeUtil.isPriType(source.getClass()) || !PrimitiveTypeUtil.isPriType(toType))) {
            return null;
        }

        /*
         * 如果都是数组类型，则构造数组
         */
        if (source != null && source.getClass().isArray() && toType.isArray()) {
            Object result;
            Class<?> componentType = toType.getComponentType();
            result = Array.newInstance(componentType, Array.getLength(source));

            for (int i = 0; i < Array.getLength(source); i++) {
                Array.set(result, i, convert(Array.get(source, i), componentType, params));
            }
            return result;
        }
        return doConvert(source, toType);
    }

    private boolean isNumberString(String stringValue) {
        if (stringValue == null || stringValue.length() == 0) {
            return false;
        }

        OUTER:
        for (char charInString : stringValue.toCharArray()) {
            for (char digit : DIGITAL_CHAR) {
                if (charInString == digit) {
                    continue OUTER;
                }
            }
            return false;
        }
        return true;
    }

    private boolean booleanValue(Object source) {
        if (source == null) {
            return false;
        }
        Class<? extends Object> c = source.getClass();
        if (c == Boolean.class) {
            return (Boolean) source;
        }
        if (c == String.class) {
            String stringValue = (String) source;
            return !(stringValue.length() == 0
                    || stringValue.equals("0")
                    || stringValue.equalsIgnoreCase("false")
                    || stringValue.equalsIgnoreCase("no")
                    || stringValue.equalsIgnoreCase("f") || stringValue
                    .equalsIgnoreCase("n"));
        }
        if (c == Character.class) {
            return ((Character) source).charValue() != 0;
        }
        if (source instanceof Number) {
            return ((Number) source).doubleValue() != 0;
        }

        return true;
    }

    private long longValue(Object source) throws NumberFormatException {
        if (source == null) {
            return 0L;
        }
        Class<? extends Object> c = source.getClass();
        if (c.getSuperclass() == Number.class) {
            return ((Number) source).longValue();
        }
        if (c == Boolean.class) {
            return ((Boolean) source).booleanValue() ? 1 : 0;
        }
        if (c == Character.class) {
            return ((Character) source).charValue();
        }

        String s = stringValue(source, true);
        return (s.length() == 0) ? 0L : Long.parseLong(s);
    }

    private double doubleValue(Object source) throws NumberFormatException {
        if (source == null) {
            return 0.0;
        }
        Class<? extends Object> c = source.getClass();
        if (c.getSuperclass() == Number.class) {
            return ((Number) source).doubleValue();
        }
        if (c == Boolean.class) {
            return ((Boolean) source).booleanValue() ? 1 : 0;
        }
        if (c == Character.class) {
            return ((Character) source).charValue();
        }
        String s = stringValue(source, true);
        return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
    }

    private BigInteger bigIntValue(Object source) throws NumberFormatException {
        if (source == null) {
            return BigInteger.valueOf(0L);
        }
        Class<? extends Object> c = source.getClass();
        if (c == BigInteger.class) {
            return (BigInteger) source;
        }
        if (c == BigDecimal.class) {
            return ((BigDecimal) source).toBigInteger();
        }
        if (c.getSuperclass() == Number.class) {
            return BigInteger.valueOf(((Number) source).longValue());
        }
        if (c == Boolean.class) {
            return BigInteger.valueOf(((Boolean) source).booleanValue() ? 1 : 0);
        }
        if (c == Character.class) {
            return BigInteger.valueOf(((Character) source).charValue());
        }

        String s = stringValue(source, true);
        return (s.length() == 0) ? BigInteger.valueOf(0L) : new BigInteger(s);
    }

    private BigDecimal bigDecValue(Object source) throws NumberFormatException {
        if (source == null) {
            return BigDecimal.valueOf(0L);
        }
        Class<? extends Object> c = source.getClass();
        if (c == BigDecimal.class) {
            return (BigDecimal) source;
        }
        if (c == BigInteger.class) {
            return new BigDecimal((BigInteger) source);
        }
        if (c.getSuperclass() == Number.class) {
            return new BigDecimal(((Number) source).doubleValue());
        }
        if (c == Boolean.class) {
            return BigDecimal.valueOf(((Boolean) source).booleanValue() ? 1 : 0);
        }
        if (c == Character.class) {
            return BigDecimal.valueOf(((Character) source).charValue());
        }

        String s = stringValue(source, true);
        return (s.length() == 0) ? BigDecimal.valueOf(0L) : new BigDecimal(s);
    }

    private String stringValue(Object source, boolean trim) {
        String result;

        if (source == null) {
            result = null;
        } else {
            result = source.toString();
            if (trim) {
                result = result.trim();
            }
        }
        return result;
    }

    private char charValue(Object source) {
        char result;

        if (source.getClass() == String.class && ((String) source).length() > 0 && !isNumberString((String) source)) {
            result = ((String) source).charAt(0);
        } else {
            result = (char) longValue(source);
        }

        return result;
    }

    private String stringValue(Object source) {
        return stringValue(source, false);
    }

    private Object doConvert(Object source, Class<?> toType) {
        Object result = null;

        if (source != null) {
            if ((toType == Integer.class) || (toType == Integer.TYPE)) {
                result = (int) longValue(source);
            } else if ((toType == Double.class) || (toType == Double.TYPE)) {
                result = doubleValue(source);
            } else if ((toType == Boolean.class) || (toType == Boolean.TYPE)) {
                result = booleanValue(source);
            } else if ((toType == Byte.class) || (toType == Byte.TYPE)) {
                result = (byte) longValue(source);
            } else if ((toType == Character.class) || (toType == Character.TYPE)) {
                result = charValue(source);
            } else if ((toType == Short.class) || (toType == Short.TYPE)) {
                result = (short) longValue(source);
            } else if ((toType == Long.class) || (toType == Long.TYPE)) {
                result = longValue(source);
            } else if ((toType == Float.class) || (toType == Float.TYPE)) {
                result = (float) doubleValue(source);
            } else if (toType == BigInteger.class) {
                result = bigIntValue(source);
            } else if (toType == BigDecimal.class) {
                result = bigDecValue(source);
            } else if (toType == String.class) {
                result = stringValue(source);
            }
        } else {
            if (toType.isPrimitive()) {
                result = PrimitiveTypeUtil.getPriDefaultValue(toType);
            }
        }
        return result;
    }

    public static PrimitiveConverter getInstance() {
        return CONVERTER;
    }

}
